home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-05
/
driverss.zip
/
ISOLINK.ASM
< prev
next >
Wrap
Assembly Source File
|
1991-01-31
|
51KB
|
1,539 lines
page 54,132
; Packet driver for BICC Data Networks' ISOLINK 4110-2 ethernet
; controller, written by
; Rainer Toebbicke
; European Organisation of Nuclear Research (CERN)
; Geneva, Switzerland
; based on the "generic" packet driver by Russell Nelson.
version equ 1 ;this is the minor version
include defs.asm ;SEE ENCLOSED COPYRIGHT MESSAGE
; BICC ISOLINK card constants
.286c ; we are at least on a 80286!
code segment para public
assume cs:code, ds:code
public begin ;makes us appear in the link map
begin equ $ ;used for alignment operations below
;Lance initialisation block, must be on word boundary
IBmode dw 00h ;mode word
IBpadr db 6 dup (0) ;physical addr
IBladrf db 8 dup (0ffh) ;logical addr filter
IBrdraL dw 0 ;Receive Descr Ring ptr
IBrdraH dw 0
IBrdraHF equ byte ptr IBrdraH+1
IBtdraL dw 0 ;Transmit Descr Ring ptr
IBtdraH dw 0
IBtdraHF equ byte ptr IBtdraH+1
CSR0 equ 0
c0_FIX equ 0000h
c0_ERR equ 8000h
c0_BABL equ 4000h
c0_CERR equ 2000h
c0_MISS equ 1000h
c0_MERR equ 0800h
c0_RINT equ 0400h
c0_TINT equ 0200h
c0_ErrClear equ c0_BABL+c0_CERR+c0_MISS+c0_MERR
c0_IDON equ 0100h
c0_INTR equ 0080h
c0_INEA equ 0040h
c0_RXON equ 0020h
c0_TXON equ 0010h
c0_TDMD equ 0008h
c0_STOP equ 0004h
c0_STRT equ 0002h
c0_INIT equ 0001h
CSR1 equ 1
CSR2 equ 2
CSR3 equ 3
RD struc ;Receive descriptor
RBadrL dw 0
RBadrH dw 0
RBbcnt dw 0 ;buffer size
RBmcnt dw 0 ;packet size
RD ends
RBflags equ byte ptr RBadrH+1
RBown equ 080h ;1=owned by Lance, 0=by host
RBerr equ 040h ;error summary bit
RBfram equ 020h
RBoflo equ 010h
RBcrc equ 008h
RBbuff equ 004h
RBstp equ 002h ;start of packet
RBenp equ 001h ;end of packet
TD struc ;Transmit descriptor
TBadrL dw 0
TBadrH dw 0
TBbcnt dw 0 ;buffer size
TBtdr dw 0 ;more flags
TD ends
TBflags equ byte ptr TBadrH+1
TBown equ 080h ;1=owned by Lance, 0=by host
TBerr equ 040h ;error summary bit
TBstp equ 002h ;Start of packet
TBenp equ 001h ;End of packet
TBbcntF equ byte ptr TBbcnt+1
public int_no
int_no dw 10,0 ;must be four bytes long for get_number.
public driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class db BLUEBOOK, IEEE8023, 0 ;from the packet spec
driver_type db 5 ;from the packet spec
driver_name db 'ISOLINK',0 ;name of the driver.
driver_function db 2
parameter_list label byte
db 1 ;major rev of packet driver
db 9 ;minor rev of packet driver
db 14 ;length of parameter list
db EADDR_LEN ;length of MAC-layer address
dw GIANT ;MTU, including MAC headers
dw MAX_MULTICAST * EADDR_LEN ;buffer size of multicast addrs
dw 0 ;(# of back-to-back MTU rcvs) - 1
dw 0 ;(# of successive xmits) - 1
int_num dw 0 ;Interrupt # to hook for post-EOI
;processing, 0 == none,
extrn sys_features: byte
public rcv_modes
rcv_modes dw 7 ;number of receive modes in our table.
dw 0
dw rcv_mode_1
dw 0
dw rcv_mode_3
dw 0,0
dw rcv_mode_6
rbfstart dw FirstDescr ;1st Rcv buffer descr
rbfcurr dw ?
tbfstart dw ? ;1st Xmit buffer descr
tbfcurr dw ?
tbfend dw ? ;end of Xmit buffers
xmt_buffsz_r dw ? ;xmit buffer sizes
xmt_buffsz_rn dw ? ;same, but times -1
pklen dw ? ;length of packet
pklen_rem dw ? ;remaining to be received
rcv_csr0 dw ?
rcvbuffp dd ?
RAP dw 0 ;Register Address Port
RDP dw 0 ;Register Data Port
options db 0
options_HMA equ 40h ;running in HMA
word_16 dw 16
extrn count_out_err:near, count_in_err:near
seg2lin proc near
; convert (ax:dx=offset:segment) to linear address (ax:dx=low:high)
push bx
push ax ;save offset
mov ax,dx
mul word_16 ;segment to linear address
pop bx
add ax,bx ;plus offset
adc dx,0
pop bx ;restore
ret
seg2lin endp
lin2seg proc near
; convert linear addr (ax:dx=low:high) to (ax:dx=offset:segment)
; with minimal offset to avoid wrap-around problems
; the 80286 can address 0-10ffefh in real mode:
cmp dl,010h ;over 1 Megabyte?
je l2sHMA ;yes, segment is 0FFFFh
push ax
shr ax,4 ;convert to paragraphs
shl dx,4+8
or dx,ax ;segment ok
pop ax
and ax,0fh ;offset
ret
l2sHMA:
; effectively subtract 0ffff0h from the linear address to form the
; offset:
mov dx,0ffffh ;this we know already
sub ax,0fff0h ;only have to do low order word
ret
lin2seg endp
; write bx to Lance control & status reg [ax]
wrcsr0 proc near
xor ax,ax ;write to CSR0
wrcsr: ;CSR in ax
mov dx,RAP ;address CSR
out dx,ax
mov dx,RDP ;data port
mov ax,bx
out dx,ax
ret
wrcsr0 endp
; read Lance control reg [ax]
rdcsr0 proc near
xor ax,ax ;read CSR 0
rdcsr: ;CSR in ax
mov dx,RAP
out dx,ax
mov dx,RDP
in ax,dx
ret
rdcsr0 endp
rcv_nxt_d proc near
; release current and advance to next receive descriptor
mov RBflags[bx],RBown ;give buffer to Lance
add bx,8
cmp bx,tbfstart ;end of ring?
jb rcv_nxt_ok ;no...
mov bx,rbfstart ;restart at first descriptor
rcv_nxt_ok:
mov rbfcurr,bx
ret
rcv_nxt_d endp
xmt_nxt_d proc near
xmt_nxt_d endp
wait_own_0 proc near
; wait for "own" bit in descriptor [bx] to clear
test TBflags[bx],TBown
jnz wo_wait ;no, have to wait
ret
wo_wait:
push cx ;save reg
mov cx,0ffffh
wo_lp1:
test TBflags[bx],TBown
jz wo_clear ;ok...
loop wo_lp1
wo_clear:
pop cx
ret
wait_own_0 endp
rcv_getaddr proc near
; obtain buffer addr from descriptor [bx]
mov ax,RBadrL[bx]
mov dx,RBadrH[bx]
call lin2seg
mov word ptr rcvbuffp,ax
mov word ptr rcvbuffp+2,dx
ret
rcv_getaddr endp
public as_send_pkt
; The Asynchronous Transmit Packet routine.
; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
; interrupts possibly enabled.
; Exit with nc if ok, or else cy if error, dh set to error number.
; es:di and interrupt enable flag preserved on exit.
as_send_pkt:
ret
public drop_pkt
; Drop a packet from the queue.
; Enter with es:di -> iocb.
drop_pkt:
assume ds:nothing
ret
public xmit
; Process a transmit interrupt with the least possible latency to achieve
; back-to-back packet transmissions.
; May only use ax and dx.
xmit:
assume ds:nothing
ret
public send_pkt
send_pkt proc near
;enter with ds:si -> packet, cx = packet length.
;exit with nc if ok, or else cy if error, dh set to error number.
assume ds:nothing
push ds ;save packet segment
push cs
pop ds
assume ds:code
; packet must be at least 64 bytes long (with fcs)...
cmp cx,RUNT
jnl send_L_ok
mov cl,RUNT
send_L_ok:
; get next buffer descriptor
mov bx,tbfcurr
call wait_own_0 ;wait until it's free
; If the next buffer is big enough to hold the packet, we will
; copy the packet and send it out.
; Otherwise we send it from the user's buffer but then have to
; wait until it is sent out.
; This may speed up fast machines on the expense of some memory
cmp cx,xmt_buffsz_r ;longer than our buffers?
jng send_copy ;no, copy the packet
send_user_buff: ;send from user's buffer
mov ax,si ;input buffer addr
pop dx ;segment
call seg2lin ;convert to linear addr
push TBadrL[bx] ;save original buffer address
push TBadrH[bx] ;...
mov TBadrL[bx],ax
mov TBadrH[bx],dx
jmp short send_send
send_copy:
mov ax,TBadrL[bx]
mov dx,TBadrH[bx] ;buffer address
call lin2seg ;convert to offset:segment
mov es,dx
mov di,ax
pop ds ;restore packet segment
assume ds:nothing
push cx ;save length
; The packet is copied two bytes at a time, starting with the even(!)
; address of the destination buffer.
shr cx,1 ;convert to words, can't be zero
rep movsw ;copy buffer
jnc send_moved ;was original length even?
movsb
send_moved:
pop cx
push cs
pop ds ;restore ds
assume ds:code
send_send:
neg cx ;length in two's complement
mov TBbcnt[bx],cx
or TBflags[bx],TBown+TBstp+TBenp
push bx ;save ring entry address
mov bx,c0_TDMD+c0_INEA
call wrcsr0 ;start transmitter immediately
pop bx ;restore ring entry addr
xor al,al ;assume no error
cmp cx,xmt_buffsz_rn ;was this a big buffer?
jnl send_next ;no, all done
call wait_own_0 ;wait for send ok
mov al,TBflags[bx] ;save flags
mov cx,xmt_buffsz_rn ;original buffer size negated
pop TBadrH[bx]
pop TBadrL[bx]
mov TBbcnt[bx],cx
; advance to next transmit descriptor
send_next:
add bx,8
cmp bx,tbfend ;end of ring?
jb xmt_nxt_ok ;no...
mov bx,tbfstart ;restart at first descriptor
xmt_nxt_ok:
mov tbfcurr,bx
send_done:
test al,TBerr ;were there problems?
jnz send_err
clc
ret
send_err:
call count_out_err
stc
ret
send_pkt endp
public get_address
get_address:
;get the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
assume ds:code
cmp cx,EADDR_LEN
jnb get_addr_ok ;buffer ok
stc
ret
get_addr_ok:
push si
mov si,offset IBpadr
rep movsb
pop si
mov cl,EADDR_LEN
clc
ret
public set_address
set_address:
;enter with ds:si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
assume ds:nothing
cmp cx,EADDR_LEN
jnb set_addr_ok ;buffer ok
mov dh,BAD_ADDRESS
stc
ret
set_addr_ok:
assume es:nothing
clc
ret
reset_mode proc near
; stop and restart the receiver
call reset_interface ;stop it
mov bx,c0_INIT ;re-init
call wrcsr0
mov cx,-1 ;wait until done
rm_lp:
call rdcsr0
test ax,c0_IDON ;ok?
jnz rm_IDON ;yes...
loop rm_lp ;else continue
rm_IDON:
mov bx,c0_STRT+c0_INEA+c0_IDON+c0_RXON+c0_TXON
call wrcsr0 ;start the receiver
ret
reset_mode endp
rcv_mode_1:
mov IBmode,0003h
jmp reset_mode
rcv_mode_3:
mov IBmode,0
jmp reset_mode
rcv_mode_6:
mov IBmode,8000h
jmp reset_mode
public set_multicast_list
set_multicast_list:
;enter with es:di ->list of multicast addresses, cx = number of bytes.
;return nc if we set all of them, or cy,dh=error if we didn't.
mov dh,NO_MULTICAST
stc
ret
public terminate
terminate:
ret
public get_multicast_list
get_multicast_list:
;return with nc, es:di ->list of multicast addresses, cx = number of bytes.
;return cy, NO_ERROR if we don't remember all of the addresses ourselves.
;return cy, NO_MULTICAST if we don't implement multicast.
mov dh,NO_MULTICAST
stc
ret
public reset_interface
reset_interface proc near
;reset the interface.
assume ds:code
mov bx,c0_STOP+c0_FIX
call wrcsr0
ret
reset_interface endp
;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type.
extrn recv_find: near
;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
extrn recv_copy: near
public recv
recv proc near
;called from the recv isr. All registers have been saved, and ds=cs.
;Upon exit, the interrupt will be acknowledged.
assume ds:code
call rdcsr0 ;obtain interrupting status
mov rcv_csr0,ax ;save
and ax,0ffffh-c0_INEA ;disable interrupts
mov bx,ax
call wrcsr0 ;clear acknowledged i-sources
test bx,c0_MISS ;out of buffers?
jz rcv_noerr ;no...
call count_in_err
rcv_noerr:
test rcv_csr0,c0_RINT ;received a packet
jz rcv_norint
rcv_rint:
call do_rcv ;process...
rcv_norint:
mov bx,c0_INEA ;re-enable interrupts
call wrcsr0
;The following is currently disabled because of perhaps
;unrelated problems: the clearing of the Int-sources is still
;done immediately after the interrupt....
;
;The interrupt sources are cleared very late increasing the
;possibility that we may handle several incoming packets without
;the processor being interrupted. However, this makes it possible
;that we miss an interrupt - so we have to check twice.
; mov bx,rcv_csr0 ;re-enable interrupts and
; call wrcsr0 ;clear acknowledged i-sources
; mov bx,rbfcurr ;current receive descriptor
; test RBflags[bx],RBown ;could have missed incoming pkt
; jnz rcv_ret ;no...
; call do_rcv ;process it, int already acked
rcv_ret:
ret
recv endp
do_rcv proc near
mov bx,rbfcurr ;address descriptor
;The following loop continues to check the next descriptor for
;work. It can therefore happen that a we already check descriptors
;while the Lance has not yet finished receiving the whole packet on
;remaining descriptors!
rcv_lp:
mov al,RBflags[bx]
test al,RBown ;something in buffer?
jz rcv_dobuf ;yes...
ret ;else return
rcv_dobuf:
test al,RBstp ;start of packet?
jz rcv_next_bx ;no, unrolling err chain
rcv_GetEnp:
test al,RBerr ;anything wrong with it?
jz rcv_ge_noerr ;no, seems ok.
mov dl,al ;copy flags
and dl,RBenp+RBoflo
cmp dl,RBenp+RBoflo ;this is no error!
je rcv_enp ;get length
jmp short rcv_do_err
rcv_ge_noerr:
test al,RBenp ;end of packet?
jnz rcv_enp ;yes, get length
; not on last segment
; test al,RBoflo ;possible error
; jnz rcv_do_err_0
add bx,8 ;to next descriptor
cmp bx,tbfstart ;wrap around?
jb rcv_ge_bx ;no...
mov bx,rbfstart
rcv_ge_bx:
cmp bx,rbfcurr ;make sure we don't loop!
je rcv_do_err_0 ;this is nonsense
mov al,RBflags[bx] ;get new flags
test al,RBown ;must be ours!
jz rcv_GetEnp ;ok, go ahead
; when we arrive here, we are processing a packet that has not yet
; been completely received. Leave everything as it is, we are
; guaranteed to check again, when the interrupt comes in at the
; latest.
ret
rcv_do_err_0:
; should count it somewhere else, but where?
rcv_do_err:
call count_in_err
rcv_next:
mov bx,rbfcurr ;restore current desc
rcv_next_bx:
call rcv_nxt_d ;next descriptor
jmp rcv_lp
;end of packet, check errors and obtain length
rcv_enp:
; test al,RBfram+RBcrc ;possible errors?
; jnz rcv_do_err
mov cx,RBmcnt[bx] ;message length
sub cx,4 ;strip FCS
mov bx,rbfcurr ;restore descriptor addr
cmp cx,GIANT ;reasonable size?
jg rcv_do_err ;rubbish!
jcxz rcv_next ;nothing to do...
mov pklen,cx ;save packet length
mov pklen_rem,cx ;remains to be rcvd
call rcv_getaddr
mov di,ax
mov es,dx
add di,EADDR_LEN*2 ;point to type field
mov dl, BLUEBOOK ;assume bluebook Ethernet.
mov ax, es:[di]
xchg ah, al
cmp ax, 1500
ja BlueBookPacket
inc di ;set di to 802.2 header
inc di
mov dl, IEEE8023
BlueBookPacket:
call recv_find ;want this packet?
mov ax,es
or ax,di
mov bx,rbfcurr ;restore in any case!
jz rcv_next_bx ;no buffer, give up
push es
push di
rcv_seg_lp:
; the packet's segments are copied into the client's buffer.
; The calculated length did not include the 4 byte fcs. Care must
; be taken not to copy the fcs, which may (in the worst case) be
; part of the last two segments!
test RBflags[bx],RBenp ;packet ends here?
jz rcv_seg_l_buf ;no, use buffer size
mov cx,pklen_rem ;remaining msg length
or cx,cx ;still data?
jle rcv_done ;no, just part of fcs
jmp short rcv_seg_l_ok
rcv_seg_l_buf:
mov cx,RBbcnt[bx] ;buffer size
neg cx ;it's in two's complement
sub pklen_rem,cx ;correct remaining len
jnl rcv_seg_l_ok ;use whole buffer
; the current buffer contains part of the fcs...
add cx,pklen_rem ;use only data part
rcv_seg_l_ok:
lds si,rcvbuffp ;point to buffer
assume ds:nothing
; The segment is copied two bytes at a time; we know that the input
; buffer starts on an even address.
shr cx,1 ;CF set when not even length!
jz rcv_last
rep movsw ;copy the packet
rcv_last:
jnc rcv_moved
movsb ;copy last byte
rcv_moved:
push cs
pop ds
assume ds:code
test RBflags[bx],RBenp ;end of packet?
jnz rcv_done ;yes...
call rcv_nxt_d ;to next descriptor
call rcv_getaddr ;next buffer addr
jmp rcv_seg_lp
rcv_done:
mov cx,pklen
pop si ;restore buffer addr
pop ds
assume ds:nothing
call recv_copy ;wake up client
push cs
pop ds
assume ds:code
;test jmp rcv_next ;do next one
mov bx,rbfcurr
call rcv_nxt_d
ret
do_rcv endp
public recv_exiting
recv_exiting:
;called from the recv isr after interrupts have been acknowledged.
;Only ds and ax have been saved.
assume ds:nothing
ret
; Most of the initialisation code is used only once and can be
; dicarded when init finishes, but not this:
; we formatted the buffer descriptors (a maximum space for
; that could be provided), but where could we place this code
; if buffers are allocated immediately afterwards?
; Too dangerous in the buffer area, since we will start the receiver now.
; Possible alternative: relocate to an address after the buffers, but
; since it's only a few bytes...
eto_go:
assume ds:code
call wrcsr ;start the Lance
pop dx ;get our ending paragraph
mov ah,31h
int 21h ;terminate, stay resident
; Provide maximum space for buffer descriptors before more
; discardable initialisation code starts, since the decriptors
; must be formatted. Buffers may follow immediately afterwards,
; but will not be used until the receiver is started.
; All resident code and data must be before this label!
; on quadword boundary...
org begin+( (($-begin+7)/8)*8 )
FirstDescr db ((128+128)*8) dup (0)
public usage_msg
usage_msg db "usage: ISOLINK [-n] [-d] [-w] <packet_int_no> <options>",CR,LF
db "options: [defaults]",CR,LF
db " /D<DMA channel #> [0]",CR,LF
db " /I<hw-int-level> [10]",CR,LF
db " /P<I/O port address> [(8)280]",CR,LF
db " /R<# recv buffs> <recv buff size> [16 256]",CR,LF
db " /T<# xmit buffs> <xmit buff size> [1 0]",CR,LF
db " /X (requires himem.sys installed)",CR,LF
db "$"
public copyright_msg
copyright_msg db "Packet driver for BICC 4110-2/3 ISOLINK controllers,"
db "version ",'0'+majver,".",'0'+version,CR,LF
db "Written by R.Toebbicke, CERN, Switzerland",CR,LF
db "* Copyright CERN, Geneva 1990 - Copyright and any other",CR,LF
db "* appropriate legal protection of these computer programs",CR,LF
db "* and associated documentation reserved in all countries",CR,LF
db "* of the world.",CR,LF,'$'
int_no_name db "Interrupt number $"
BaseName db "I/O Port address $"
BasePrt dw 08280h,0
DMAName db "DMA channel number $"
DMAPrt dw 0,0
extrn our_isr: near, their_isr: dword
extrn packet_int_no: byte
extrn phd_environ: word
extrn decout: near
rcv_buffno dw 16,0
rcv_buffnoname db "Number of receive buffers $"
rcv_bufflog db 4
rcv_buffsz dw 256,0
rcv_buffszname db "Receive buffer size $"
xmt_buffno dw 1,0
xmt_buffnoname db "Number of transmit buffers $"
xmt_bufflog db 0
xmt_buffsz dw 0,0
xmt_buffszname db "Transmit buffer size $"
orgseg dw ?
linaddrL dw 0
linaddrH dw 0
; keep these two together!
HMAaddr dw 00010h
HMAaddrS dw 0FFFFh
XMSctl dd ?
PS2IO dw 08280h, 09250h, 0a390h, 0b1d0h
PS2INT db 9, 10, 11, 15, 3, 4, 5, 0
extrn set_recv_isr: near
;enter with si -> argument string, di -> word to store.
;if there is no number, don't change the number.
extrn get_number: near
;enter with dx -> name of word, di -> dword to print.
extrn print_number: near
extrn skip_blanks: near
argerr db "Error in options: '"
argerrc db " "
db "'.",CR,LF,"$"
parse_err db 0
public parse_args
parse_args proc near
; If we run on a PS/2, read the default configuration from the POS regs
test sys_features,MICROCHANNEL
jnz do_mc_defaults
jmp parse_args_l
do_mc_defaults:
mov bx,08h ;1st slot
pa_slot_l:
mov dx,96h
mov ax,bx
out dx,al ;put in setup mode
mov dx,101h
in al,dx
xchg al,ah ;high id byte
dec dx
in al,dx ;low id byte
cmp ax,0808h ;ISOLINK card?
je pa_found_iso ;yes...
inc bx ;next slot
cmp bx,0fh ;too far?
jna pa_slot_l ;no, try next
mov dx,96h
xor al,al
out dx,al ;exit setup mode
jmp parse_args_l
pa_found_iso:
mov dx,102h
in al,dx ;get I/O address code
xor ah,ah
and al,6
mov bx,ax
mov ax,PS2IO[bx] ;I/O port address
mov BasePrt,ax
mov dx,104h
in al,dx
xor ah,ah
shr al,5 ;isolate interrupt bits
mov bx,ax
mov al,PS2INT[bx] ;interrupt number
mov int_no,ax
parse_args_l:
call skip_blanks
cmp al,CR ;end of args?
je pa_ret ;yes...
cmp al,'/' ;option following?
je pa_doopt ;yes
pa_err:
mov argerrc,al
mov dx,offset argerr
pa_err2:
mov ah,9
int 21h ;print error message
mov parse_err,1 ;indicate error
pa_ret:
ret
pa_doopt:
inc si
lodsb ;get option byte
cmp al,'a' ;in lowercase range?
jna pa_upper
sub al,20h ;convert to uppercase
pa_upper:
cmp al,'I'
je pa_doInt
cmp al,'P'
je pa_doPort
cmp al,'D'
je pa_doDMA
cmp al,'R'
je pa_doRcv
cmp al,'T'
je pa_doTrans
cmp al,'X' ;into extended memory?
je pa_doext
jmp pa_err
pa_doInt:
mov di,offset int_no
call get_number
jmp parse_args_l
pa_doPort:
mov di,offset BasePrt
call get_number
jmp parse_args_l
pa_doDMA:
mov di,offset DMAPrt
call get_number
jmp parse_args_l
pa_doRcv:
mov di,offset rcv_buffno
call get_number
mov di,offset rcv_buffsz
call get_number
jmp parse_args_l
pa_doTrans:
mov di,offset xmt_buffno
call get_number
mov di,offset xmt_buffsz
call get_number
jmp parse_args_l
pa_doext: ;set up for HMA
or options,options_HMA ;indicate we want to run up there
jmp parse_args_l
parse_args endp
public print_parameters
print_parameters proc
mov di,offset int_no
mov dx,offset int_no_name
call print_number
mov di,offset BasePrt
mov dx,offset BaseName
call print_number
mov di,offset DMAPrt
mov dx,offset DMAName
call print_number
mov di,offset rcv_buffno
mov dx,offset rcv_buffnoname
call print_number
mov di,offset rcv_buffsz
mov dx,offset rcv_buffszname
call print_number
mov di,offset xmt_buffno
mov dx,offset xmt_buffnoname
call print_number
mov di,offset xmt_buffsz
mov dx,offset xmt_buffszname
call print_number
ret
print_parameters endp
inv_DMA db "Invalid DMA channel number.",CR,LF,"$"
init_errmsg db "Lance initialisation failed.",CR,LF,"$"
inv_buff db "Invalid buffer specification.",CR,LF,"$"
HMAnoHIMEM db "HMA not available: HIMEM.SYS not installed.",CR,LF,"$"
HMAnotavail db "HMA not available.",CR,LF,"$"
HMAalloc db "Using extended memory (HMA).",CR,LF,"$"
HMAtoobig db "Buffers exceed HMA size.",CR,LF,"$"
HMAmsg_1 db "/X configured $"
HMAmsg_2 db " receive buffers of length $"
HMAmsg_3 db "."
crlf_msg db CR,LF,"$"
really_bad_msg db "This driver may be buggy. Do you really want to run it (y/n)? ",'$'
public etopen
etopen proc near
assume ds:code
mov dx,offset really_bad_msg
mov ah,9
int 21h
mov ah,7
int 21h
cmp al,'y'
je etopen_1
cmp al,'Y'
je etopen_1
int 20h
etopen_1:
mov dx,offset crlf_msg
mov ah,9
int 21h
test parse_err,0ffh ;any parsing error?
jnz eto_errexit2 ;yes, give up
cmp packet_int_no,0
mov dx,offset usage_msg
jz eto_errexit
; check DMA port
mov ax,DMAPrt
or ax,ax
jb eto_invalid_DMA ;should not be negative
cmp ax,3
jna eto_DMA_ok
eto_invalid_DMA:
mov dx,offset inv_DMA
eto_errexit:
mov ah,9
int 21h
eto_errexit2:
stc
ret
eto_DMA_ok:
; check receive buffers
mov bx,rcv_buffno
call do_power_of_2
cmp ax,128
jg eto_inv_Buff
or ax,ax ;must have something!
jz eto_inv_Buff
mov rcv_bufflog,cl
mov rcv_buffno,ax
mov ax,rcv_buffsz ;check buffer size
cmp ax,100 ;at least that big
jg eto_rcvsz_1
mov ax,100
eto_rcvsz_1:
cmp ax,GIANT+4 ;biggest packet plus fcs
jl eto_rcvsz_2 ;does not exceed
mov ax,GIANT+4 ;take maximum
eto_rcvsz_2:
mov rcv_buffsz,ax
; check transmit buffers
mov bx,xmt_buffno
call do_power_of_2
or ax,ax ;must have something!
jz eto_inv_Buff
cmp ax,128
jna eto_xmt_buffok
eto_inv_Buff:
mov dx,offset inv_Buff
jmp eto_errexit
eto_xmt_buffok:
mov xmt_bufflog,cl
mov xmt_buffno,ax
mov ax,xmt_buffsz ;check buffer size
cmp ax,RUNT ;at least that big
jg eto_xmtsz_1
mov ax,0 ;else don't use
eto_xmtsz_1:
cmp ax,GIANT ;biggest packet w/o fcs
jl eto_xmtsz_2 ;does not exceed
mov ax,GIANT ;take maximum
eto_xmtsz_2:
mov xmt_buffsz,ax
mov xmt_buffsz_r,ax ;again for our resident part
neg ax
mov xmt_buffsz_rn,ax ;times -1 for resident part
; set RAP & RDP addresses
mov ax,BasePrt
add ax,0ch
mov RDP,ax
add ax,02h
mov RAP,ax
; this code is from Russell Nelson's packet driver skeleton.
; Need it here, since we won't return from etopen: as soon as the
; receiver is started, this place may get clobbered with Ethernet packets
mov ah,35h ;remember their packet interrupt.
mov al,packet_int_no
int 21h
mov their_isr.offs,bx
mov their_isr.segm,es
mov ah,25h ;install our packet interrupt
mov dx,offset our_isr
int 21h
; First of all: stop the Lance, sets required bits in CSR3 as well
call reset_interface
call set_recv_isr ;intercept interrupts
; Now we decide whether we will run in the HMA (above 1 MB) before
; we do any calculations involving real addresses
test options,options_HMA ;will we?
jnz eto_tryHMA ;yes
jmp eto_HMAok ;no...
; first check of HIMEM.SYS is installed and if we can globally
; enable the HMA
eto_tryHMA:
mov ax,4300h
int 2Fh ; Is an XMS Driver installed?
mov dx,offset HMAnoHIMEM ;error message in case...
cmp al,80h
jne eto_noHMA ;no...
mov ax,4310h
int 2fh ;obtain XMS control addr
mov word ptr XMSctl,bx ;save entry point
mov word ptr XMSctl+2,es
mov dx,0ffffh ;allocate whole HMA
mov ah,1
call XMSctl
mov dx,offset HMAnotavail ;error msg in case...
cmp ax,1
jne eto_noHMA ;not available
mov ah,3 ;global enable A20 line
call XMSctl
cmp ax,1 ;ok?
je eto_HMA_enabled
eto_noHMA:
mov ah,9
int 21h ;error msg addr in dx!
and options,not options_HMA ;turn off HMA bit
jmp eto_HMAok ;continue as usual
eto_HMA_enabled:
; mov dx,offset HMAalloc
; mov ah,9
; int 21h ;confirm
; maximize the receive buffer space to utilise the whole HMA:
; 1. calculate remaining space excluding receive buffers and descriptors
; 2. start with 128 receive buffers
; 3. calculate remaining space for buffers
; 4. divide by number of buffers to obtain (even) buffer size
; 5. if size inferior to defaulted or specified size half number
; of buffers and restart on step 2.
mov bx,0fff0h ;total HMA size
sub bx,offset FirstDescr ;minus resident code
mov ax,xmt_buffsz ;xmit buffer size
add ax,8 ;plus descriptor
mul xmt_buffno ;=total xmit space
or dx,dx ;not more than 64k!
jnz try_err ;ooops
sub bx,ax ;remaining space without recv
jc try_err ;must be positive!
mov cx,128 ;try 128 buffers
try_lp:
mov ax,cx ;number of buffers
shl ax,3 ;8 bytes long
sub ax,bx
neg ax ;remaining space for buffers
xor dx,dx ;accumulator extension
div cx ;calculate buffer length
and ax,0fffeh ;even length
cmp ax,rcv_buffsz ;long enough?
jnb try_ok ;yes...
shr cx,1 ;else half number of buffers
jnz try_lp ;and retry
try_err:
mov dx,offset HMAtoobig
jmp eto_errexit
try_ok:
cmp ax,GIANT+4 ;maximum size
jng try_ok_2 ;does not exceed
mov ax,GIANT+4 ;else maximum length
try_ok_2:
mov rcv_buffno,cx
mov rcv_buffsz,ax
mov bx,cx
call do_power_of_2 ;have to do it again
mov rcv_bufflog,cl
mov dx,offset HMAmsg_1
mov ah,9
int 21h
mov ax,rcv_buffno
xor dx,dx
call decout
mov dx,offset HMAmsg_2
mov ah,9
int 21h
mov ax,rcv_buffsz
xor dx,dx
call decout
mov dx,offset HMAmsg_3
mov ah,9
int 21h
; la demarche a suivre:
; we copy ourselves into the HMA completely, format all the
; descriptors, correct the interrupt addr and exit to DOS
mov ax,cs
mov orgseg,ax ;save original segment
les di,dword ptr HMAaddr ;that's where we will be
mov cx,offset end_code ;length(!) of code to be copied
mov si,10h ;start at offset 10
sub cx,si ;correct length
rep movsb ;copy us
push HMAaddrS ;our new segment
; careful: here comes a jump!
call setcs ;we're in the HMA
push cs
pop ds ;our data as well
eto_HMAok:
; get linear address for receive buffer descriptor start
push ds
mov ax,offset FirstDescr
pop dx
call seg2lin
; Store in Init Block
mov IBrdraL,ax
mov IBrdraH,dx
; calculate linear address of first buffer
mov bx,offset FirstDescr
mov rbfstart,bx
mov rbfcurr,bx
mov ax,rcv_buffno
push ds
pop dx
add ax,xmt_buffno ;result is still in segment
shl ax,3 ;times eight
add ax,bx ;won't leave segment!
call seg2lin ;convert to linear addr
mov linaddrL,ax ;first available buffer addr
mov linaddrH,dx
; format receive buffer descriptors
mov si,rcv_buffsz ;buffer size
mov cx,rcv_buffno ;number of buffers
mov dl,RBown ;flags
eto_rcvloop:
call format_descr
add bx,8
loop eto_rcvloop
;set transmit descriptor ring address
push ds
mov ax,bx
pop dx
call seg2lin
mov IBtdraL,ax
mov IBtdraH,dx
; format transmit buffer descriptors
mov tbfstart,bx ;remember start
mov tbfcurr,bx
mov dl,0 ;initial flags
mov cx,xmt_buffno ;number of buffers
mov si,xmt_buffsz ;buffer size
eto_xmtloop:
call format_descr
add bx,8
loop eto_xmtloop
mov tbfend,bx ;remember end
; finish initialisation block
mov al,rcv_bufflog ;# of rcv buffers
shl al,5
mov IBrdraHF,al
mov al,xmt_bufflog ;# of xmt buffers
shl al,5
mov IBtdraHF,al
; copy Ethernet address out of ROM
mov cx,6
push ds
pop es
mov di,offset IBpadr
mov dx,BasePrt
eto_addrloop:
in ax,dx
inc dx
inc dx
stosb
loop eto_addrloop
; init DMA controller
test sys_features,MICROCHANNEL ;running on a PS/2?
jnz eto_DMA_done ;yes, no DMA
mov al,0d0h ;mode reg bits
mov dx,0bh ;addr of mode reg
or al,byte ptr DMAPrt ;or in DMA number
out dx,al
jmp $+2
and al,3 ;leave only DMA channel #
mov dx,0ah ;mask register addr
out dx,al ;clear the DMA channel
eto_DMA_done:
; set initialisation block addr
push ds
pop dx
mov ax,offset IBmode
call seg2lin
push dx ;don't loose it
mov bx,ax
mov ax,CSR1
call wrcsr ;low order 16 bits of addr
pop bx
mov ax,CSR2
call wrcsr ;high order bits
; init the controller and wait for completion
mov bx,c0_FIX+c0_INIT
call wrcsr0
mov cx,-1 ;wait for completion
eto_initlp:
call rdcsr0
test ax,c0_IDON ;done?
jnz eto_init_ok ;good!
loop eto_initlp
mov dx,offset init_errmsg
call outofHMA ;get out of HMA
jmp eto_errexit
eto_init_ok:
test options,options_HMA ;are we in HMA?
jz eto_tsr ;no, use TSR code
xor ax,ax
; correct the packet and hardware interrupt addresses
mov es,ax
xor bh,bh
mov bl,packet_int_no
shl bx,2 ;point to interrupt vector
push cs
pop es:[bx+2]
mov bx,int_no ;card interrupt
add bx,68h ;assume above 8
cmp bx,8+68h ;is it?
jnb eto_HMA_int_ok ;yes...
sub bx,60h ;PS/2 can use interrupt < 8
eto_HMA_int_ok:
shl bx,2 ;point to interrupt vector
push cs
pop es:[bx+2]
; Now start the receiver and exit to DOS
call outofHMA ;out of danger zone (buffers!)
mov bx,c0_STRT+c0_INEA+c0_IDON+c0_RXON+c0_TXON ; ...SET...
call wrcsr0 ; ...GO!
int 20h ;exit...
eto_tsr:
;calculate end of resident part (relative to pgm start)
mov ax,linaddrL
mov dx,linaddrH
add ax,15 ;round to paragraph
adc dl,0
shr ax,4
shl dl,4
or ah,dl
mov dx,ds ;minus pgm start addr
sub ax,dx
push ax ;save the address
; again some code from the packet driver skeleton...
mov ah,49h ;free our environment, because
mov es,phd_environ ; we won't need it.
int 21h
mov bx,1 ;get the stdout handle.
mov ah,3eh ;close it in case they redirected it.
int 21h
mov ax,csr0 ; READY...
mov bx,c0_STRT+c0_INEA+c0_IDON+c0_RXON+c0_TXON ; ...SET...
jmp eto_go ; ...GO!
etopen endp
do_power_of_2 proc near
; adjust bx to valid # of buffers.
; on exit: ax=fitting number, cx=log2(ax)
mov cx,7 ;allow 7 shifts
mov ax,bx
dpw2_loop:
cmp ax,1
je dpw2_done
shr ax,1
loop dpw2_loop
dpw2_done:
mov ax,7
sub ax,cx ;get number of shifts
mov cx,ax
mov ax,1
jcxz dpw2_ret
shl ax,cl
dpw2_ret:
ret
do_power_of_2 endp
format_descr proc near
; format buffer descriptor pointed to by [bx]
; si=buffer size, dl=flags
push dx ;save flags
mov ax,linaddrL ;next available buffer addr
mov dx,linaddrH
mov RBadrL[bx],ax ;buffer addr
mov RBadrH[bx],dx
add ax,si ;point to next available addr
adc dx,0
mov linaddrL,ax
mov linaddrH,dx
mov ax,si ;get buffer size
; or ah,0f0h ;reserved bits to 1
neg ax ;two's complement
mov RBbcnt[bx],ax
pop dx ;restore flags
mov RBflags[bx],dl
ret
format_descr endp
outofHMA proc near ;leave the HMA
test options,options_HMA ;are we in it?
jz ooHret ;no...
push orgseg
call setcs
push cs
pop ds
ooHret:
ret
outofHMA endp
setcs proc near
db 0cbh ;RET FAR (!)
setcs endp
end_code equ $
code ends
end